﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using zPod.zInject;
using System.IO;
using Mono.Cecil;
using System.Reflection;

namespace zInject.UnitTest
{
	/// <summary>
	/// Summary description for Injector
	/// </summary>
	[TestClass]
	public class InjectorTests
	{
		public InjectorTests()
		{
			
		}

		private TestContext testContextInstance;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext
		{
			get
			{
				return testContextInstance;
			}
			set
			{
				testContextInstance = value;
			}
		}


		const string OriginalAssemblyPath = @"..\..\..\zInject.Testing\bin\Debug\zInject.Testing.dll";
		const string InjectedAssemblyPath = @".\zInject.Testing.Injected.dll";

		static Assembly InjectedAssembly;

		#region Additional test attributes
		[ClassInitialize()]
		public static void Initialize(TestContext testContext) 
		{
			if (File.Exists(InjectedAssemblyPath))
			{
				File.Delete(InjectedAssemblyPath);
			}

			Injector injector = new Injector();
			var newAssembly = injector.Inject(OriginalAssemblyPath);
			AssemblyFactory.SaveAssembly(newAssembly, InjectedAssemblyPath);

			InjectedAssembly = Assembly.LoadFile(Path.GetFullPath(InjectedAssemblyPath));
		}

		[ClassCleanup()]
		public static void Cleanup()
		{
		}
		
		// Use TestInitialize to run code before running each test 
		// [TestInitialize()]
		// public void MyTestInitialize() { }
		//
		// Use TestCleanup to run code after each test has run
		// [TestCleanup()]
		// public void MyTestCleanup() { }
		
		#endregion

		[TestMethod]
		public void NoBehaviorAttributes()
		{
			var type = InjectedAssembly.GetExportedTypes().First();

			Assert.IsTrue(type.GetMethod("NoBehavior").GetCustomAttributes(true).Count() == 0);
		}

		[TestMethod]
		public void InjectedAttribute()
		{
			var type = InjectedAssembly.GetExportedTypes().First();

			Assert.IsTrue(type.GetMethod("OneBehavior")
				.GetCustomAttributes(true)
				.Where(ca => ca.GetType() == typeof(InjectedAttribute))
				.Count() == 1);
		}

		[TestMethod]
		public void OneBehaviorExecution()
		{
			var type = InjectedAssembly.GetExportedTypes().First();

			DateTime before = PriorityOneAttribute.BeforeCall;
			DateTime after = PriorityOneAttribute.AfterCall;

			var obj = Activator.CreateInstance(type);
			type.GetMethod("OneBehavior").Invoke(obj, new object[0]);

			Assert.IsTrue(before < PriorityOneAttribute.BeforeCall);
			Assert.IsTrue(after < PriorityOneAttribute.AfterCall);
			Assert.IsTrue(PriorityOneAttribute.BeforeCall < PriorityOneAttribute.AfterCall);
		}

		[TestMethod]
		public void MultipleBehaviorExecution()
		{
			var type = InjectedAssembly.GetExportedTypes().First();

			var obj = Activator.CreateInstance(type);
			type.GetMethod("TwoBehaviors").Invoke(obj, new object[0]);

			Assert.IsTrue(PriorityOneAttribute.BeforeCall > PriorityTwoAttribute.BeforeCall);
			Assert.IsTrue(PriorityOneAttribute.AfterCall < PriorityTwoAttribute.AfterCall);
		}

		[TestMethod]
		public void OneBehaviorOnPropertyExecution()
		{
			var type = InjectedAssembly.GetExportedTypes().First();

			DateTime before = PriorityOneAttribute.BeforeCall;
			DateTime after = PriorityOneAttribute.AfterCall;

			var obj = Activator.CreateInstance(type);
			type.GetProperty("Prop").SetValue(obj, 1, new object[0]);

			Assert.IsTrue(before < PriorityOneAttribute.BeforeCall);
			Assert.IsTrue(after < PriorityOneAttribute.AfterCall);
			Assert.IsTrue(PriorityOneAttribute.BeforeCall < PriorityOneAttribute.AfterCall);
		}

	}
}
